<!--
 * Copyright ©
 * #  
 * @author: zw
 * @date: 2023-02-26 
 -->


<template>
  <div class="wrap" v-calcul-left v-drag>
    <span class="marker" />

    <ul class='g6-component-toolbar z-20' v-search>
      <li class='g6-component-toolbar-item'><el-button type="text" class="iconfont icon-sousuo" /></li>
      <li class="input-wrap" :style="{ height: searchParams.stack.length <= 0 ? '40px': '65px' }">

        <el-form @submit.native.prevent>

          <el-form-item class="m-0 ">
            <el-input ref="ElInput" v-model="keywords" placeholder="请输入检索条件" clearable @keyup.enter.native="$emit('search', keywords)">
              <el-tooltip effect="dark" slot="prefix" content="折叠" placement="bottom-end">
                <el-button size="mini" class="h-full" type="text" icon="el-icon-arrow-left" @click="closed" />
              </el-tooltip>
              <template #append>
                <el-button size="mini" type="primary" @click="$emit('search', keywords)">搜索</el-button>
              </template>
            </el-input>
            <div class="counter">
              <p>第{{ searchParams.step + 1 }}项，共{{ searchParams.stack.length }}项</p>
              <p class="btn-control" v-show="searchParams.stack.length > 1">
                <el-button type="text" size="mini" class="pt-0 pb-2" @click="$emit('prev')">上一个</el-button>
                <el-button type="text" size="mini" class="pt-0 pb-2" @click="$emit('next')">下一个</el-button>
              </p>
            </div>
          </el-form-item>

        </el-form>
      </li>

    </ul>

    <ul class='g6-component-toolbar' v-for="list, ii in group" :key="ii">
      <li v-for='item, index in list' :key='index' class='g6-component-toolbar-item'>
        <el-tooltip effect="dark" :content="item.code" placement="bottom-end">
          <el-button type="text" :class="['iconfont', item.icon]" @click="handleClick(item)" :disabled="calculDisabled(item)" />
        </el-tooltip>
      </li>
    </ul>

  </div>
</template>

<script>
import { clone } from '@antv/util';
import { expandAnimate, shrinkAnimate } from '@/utils';
import { requestFullScreen, exitFullScreen, isFullScreen, isFullScreenSupported } from '@/utils/fullScreen';
export default {
  name: 'toolBar',
  data() {
    return {
      keywords: '',
      group: [
        [
          { code: 'undo', icon: 'icon-zhongzuo', label: '撤销' },
          { code: 'redo', icon: 'icon-zhongzuo1', label: '重做' },
          { code: 'save', icon: 'icon-baocun', label: '保存' },
        ],
        [
          { code: 'zoomIn', icon: 'icon-fangda', label: '放大' },
          { code: 'zoomOut', icon: 'icon-suoxiao', label: '缩小' },
          { code: 'fitView', icon: 'icon-daohang', label: '适应画布' },
        ],
        [
          { code: 'fullScreen', icon: 'icon-quanping', label: '全屏' },
        ]
      ]
    };
  },

  props: {
    graph: { type: Object, required: true },
    zoomSensitivity: { type: Number, default: 2 },
    searchParams: { type: Object, required: true },
  },

  mounted() { },

  methods: {
    handleClick(item) {
      switch (item.code) {
        case 'save':

          break;
        case 'undo':
          this.undo();
          break;
        case 'redo':
          this.redo();
          break;
        case 'zoomIn':
          this.zoomIn();
          break;
        case 'zoomOut':
          this.zoomOut();
          break;
        case 'fitView':
          this.fitView();
          break;
        case 'reset':
          break;
        case 'fullScreen':
          this.fullScreen();
          break;
        default:
          break;
      }
    },

    zoomOut() {
      const { graph, zoomSensitivity } = this;
      const currentZoom = graph.getZoom();
      const ratioOut = 1 / (1 - 0.05 * zoomSensitivity);
      const maxZoom = graph.get('maxZoom');
      if (ratioOut * currentZoom > maxZoom) return;
      graph.zoomTo(currentZoom * ratioOut);
    },

    zoomIn() {
      const { graph, zoomSensitivity } = this;
      const currentZoom = graph.getZoom();
      const ratioIn = 1 - 0.05 * zoomSensitivity;
      const minZoom = graph.get('minZoom');
      if (ratioIn * currentZoom < minZoom) return;
      graph.zoomTo(currentZoom * ratioIn);
    },

    undo() {
      const { graph } = this;
      const undoStack = graph.getUndoStack();
      if (!undoStack || !undoStack.length) return;

      const currentData = undoStack.pop();
      if (!currentData) return;

      const action = currentData.action;
      graph.pushStack(action, clone(currentData.data), 'redo');
      let data_1 = currentData.data.before;
      if (action === 'add') data_1 = currentData.data.after;

      if (!data_1) return;

      switch (action) {

        case 'visible':
          Object.keys(data_1).forEach((key) => {
            const array = data_1[key];
            if (!array) return;
            array.forEach((model) => {
              const item = graph.findById(model.id);
              if (model.visible) {
                graph.showItem(item, false);
              } else {
                graph.hideItem(item, false);
              }
            });
          });
          break;

        case 'render':

        case 'update':
          Object.keys(data_1).forEach((key) => {
            const array = data_1[key];
            if (!array) return;
            array.forEach((model) => {
              const item = graph.findById(model.id);
              delete model.id;
              graph.updateItem(item, model, false);
              if (item.getType() === 'combo') graph.updateCombo(item);
            });
          });
          break;

        case 'changedata':
          graph.changeData(data_1, false);
          break;

        case 'delete':
          Object.keys(data_1).forEach((key) => {
            const array = data_1[key];
            if (!array) return;
            array.forEach((model) => {
              const itemType = model.itemType;
              delete model.itemType;
              graph.addItem(itemType, model, false);
            });
          });
          break;

        case 'add':
          Object.keys(data_1).forEach((key) => {
            const array = data_1[key];
            if (!array) return;
            array.forEach((model) => {
              graph.removeItem(model.id, false);
            });
          });
          break;

        case 'updateComboTree':
          Object.keys(data_1).forEach((key) => {
            const array = data_1[key];
            if (!array) return;
            array.forEach((model) => {
              graph.updateComboTree(model.id, model.parentId, false);
            });
          });
          break;

        case 'createCombo':
          const afterCombos = currentData.data.after.combos;
          const createdCombo = afterCombos[afterCombos.length - 1];
          Object.keys(data_1).forEach((key) => {
            const array = data_1[key];
            if (!array) return;
            array.forEach((model) => {
              graph.updateComboTree(model.id, model.parentId, false);
            });
          });
          graph.removeItem(createdCombo.id, false);
          break;

        case 'uncombo':
          const targetCombo_1 = data_1.combos[data_1.combos.length - 1];
          const childrenIds = [...data_1.nodes, ...data_1.combos].map(child => child.id).filter(id => id !== targetCombo_1.id);
          graph.createCombo(targetCombo_1, childrenIds, false);
          break;

        case 'layout':
          graph.updateLayout(data_1, undefined, undefined, false);
          break;

        default:
          break;
      }
    },

    redo() {
      const { graph } = this;
      const redoStack = graph.getRedoStack();
      if (!redoStack || !redoStack.length) return;

      const currentData = redoStack.pop();
      if (!currentData) return;

      const action = currentData.action;
      let data_2 = currentData.data.after;
      graph.pushStack(action, clone(currentData.data));
      if (action === 'delete') data_2 = currentData.data.before;

      if (!data_2) return;

      switch (action) {

        case 'visible':
          Object.keys(data_2).forEach((key) => {
            const array = data_2[key];
            if (!array) return;
            array.forEach((model) => {
              const item = graph.findById(model.id);
              if (model.visible) {
                graph.showItem(item, false);
              } else {
                graph.hideItem(item, false);
              }
            });
          });
          break;

        case 'render':

        case 'update':
          Object.keys(data_2).forEach((key) => {
            var array = data_2[key];
            if (!array) return;
            array.forEach((model) => {
              var item = graph.findById(model.id);
              delete model.id;
              graph.updateItem(item, model, false);
              if (item.getType() === 'combo') graph.updateCombo(item);
            });
          });
          break;

        case 'changedata':
          graph.changeData(data_2, false);
          break;

        case 'delete':
          if (data_2.edges) {
            data_2.edges.forEach((model) => {
              graph.removeItem(model.id, false);
            });
          }
          if (data_2.nodes) {
            data_2.nodes.forEach((model) => {
              graph.removeItem(model.id, false);
            });
          }
          if (data_2.combos) {
            data_2.combos.forEach((model) => {
              graph.removeItem(model.id, false);
            });
          }
          break;

        case 'add':
          Object.keys(data_2).forEach((key) => {
            const array = data_2[key];
            if (!array) return;
            array.forEach((model) => {
              const itemType = model.itemType;
              delete model.itemType;
              graph.addItem(itemType, model, false);
            });
          });
          break;

        case 'updateComboTree':
          Object.keys(data_2).forEach((key) => {
            const array = data_2[key];
            if (!array) return;
            array.forEach((model) => {
              graph.updateComboTree(model.id, model.parentId, false);
            });
          });
          break;

        case 'createCombo':
          const createdCombo = data_2.combos[data_2.combos.length - 1];
          graph.createCombo(createdCombo, createdCombo.children.map(child => child.id), false);
          break;

        case 'uncombo':
          const beforeCombos = currentData.data.before.combos;
          const targertCombo = beforeCombos[beforeCombos.length - 1];
          graph.uncombo(targertCombo.id, false);
          break;

        case 'layout':
          graph.updateLayout(data_2, undefined, undefined, false);
          break;

        default: break;
      }
    },

    fullScreen() {
      if (!isFullScreenSupported(document)) return;
      const { graph } = this;
      const container = graph.get('container');
      const parent = container.parentNode;
      const hasFull = isFullScreen(document);
      if (!hasFull) {
        requestFullScreen(parent);
      } else {
        exitFullScreen(document);
      }

    },

    fitView() {
      const { graph } = this;
      graph.fitView([20, 20], true, { duration: 200, easing: 'easeCubic' });
    },

    closed() {
      this.$refs.ElInput.clear();
      this.$emit('closed');
      this.$emit('search', this.keywords);
    },
  },

  computed: {
    calculDisabled() {
      return (item) => {
        const { graph } = this;
        if (!graph.get) return false;

        const undoStack = graph.getUndoStack();
        if (item.code === 'undo') return !undoStack.length;
        const redoStack = graph.getRedoStack();
        if (item.code === 'redo') return !redoStack.length;

        return false;
      };
    },
  },

  directives: {
    drag: {
      bind(el, binding, vnode) {
        const marker = el.querySelector('.marker');
        const siblings = Array.from(el.children).filter(item => item !== marker);
        let func = () => { };

        marker.addEventListener('mousedown', ev => {
          ev.preventDefault();
          siblings.forEach(item => item.style.pointerEvents = 'none');
          document.addEventListener('mousemove', mousemove.call(null, ev), false);
          document.addEventListener('mouseup', mouseup, false);

        }, false);

        function mousemove(ev) {
          const disX = ev.clientX - el.offsetLeft;
          const disY = ev.clientY - el.offsetTop;

          func = (e) => {
            let dx, dy;
            dx = e.clientX - disX;
            if (dx <= 0) dx = 0;
            if (dx >= el.parentNode.offsetWidth - (el.offsetWidth || 100)) dx = el.parentNode.offsetWidth - (el.offsetWidth || 100);
            dy = e.clientY - disY;
            if (dy <= 0) dy = 0;
            if (dy >= el.parentNode.offsetHeight - (el.offsetHeight || 45)) dy = el.parentNode.offsetHeight - (el.offsetHeight || 45);
            el.style.top = dy + 'px';
            el.style.left = dx + 'px';
          };

          return func;
        }

        function mouseup(e) {
          siblings.forEach(item => item.style.pointerEvents = 'auto');
          document.removeEventListener('mousemove', func);
          document.removeEventListener('mouseup', mouseup);
        }

      }
    },
    search: {
      bind(el, binding, vnode) {
        const btn = el.querySelector('.el-button');
        const inputContainer = el.querySelector('.input-wrap');
        let isExpand = false;

        vnode.context.$on('closed', () => {
          shrinkAnimate({
            duration: 180,
            callback(rate) {
              inputContainer.style.width = `${360 * rate}px`;
            },
            finish() {
              inputContainer.style.width = '0px';
              inputContainer.style.border = 'none';
              inputContainer.style.boxShadow = 'none';
            }
          });
          isExpand = false;
        });

        btn.addEventListener('click', (e) => {
          e.stopPropagation();
          isExpand = true;
          expandAnimate({
            duration: 180,
            callback(rate) {
              inputContainer.style.width = `${360 * rate}px`;
              inputContainer.style.border = '1px solid #1890ff';
              inputContainer.style.boxShadow = '0 0 2px #1890ff';
            },
            finish() { }
          });
        }, false);

        function clickoutside(e) {
          e.stopPropagation();
          const { keywords } = vnode.context;
          if (!isExpand) return;
          if (keywords.length > 0) return;
          if (el.contains(e.target) || (e.target.classList.contains('el-input__icon') && e.target.classList.contains('el-icon-circle-close'))) return;

          shrinkAnimate({
            duration: 180,
            callback(rate) {
              inputContainer.style.width = `${360 * rate}px`;
            },
            finish() {
              inputContainer.style.width = '0';
              inputContainer.style.border = 'none';
              inputContainer.style.boxShadow = 'none';
            }
          });
          isExpand = false;

        }

        el._clickoutside = clickoutside;
        document.addEventListener('click', clickoutside, false);

      },
      unbind(el, binding, vnode) {
        vnode.context.$off('closed');
        document.removeEventListener('click', el._clickoutside, false);
      }
    },
    calculLeft: {
      bind(el, binding, vnode) {
        vnode.context.$nextTick(() => {
          const children = Array.from(el.children).filter(child => !child.classList.contains('marker'));
          const widths = children.map(bar => bar.getBoundingClientRect());
          children.forEach((bar, index) => bar.style.left = `${widths.slice(0, index).reduce((pre, cur) => pre + cur.width + 8, 0)}px`);
        });
      }
    }
  }
  //  End
}

</script>

<style lang='scss' scoped>
.wrap {
  position: absolute;
  top: 16px;
  left: 16px;
  .marker {
    position: absolute;
    top: -6px;
    left: -1px;
    border-top: 10px solid transparent;
    border-bottom: 10px solid transparent;
    border-right: 10px dashed #ccc;
    z-index: 20;
    transform: rotate(45deg);
    shape-outside: polygon(0 0, 100% 0, 100% 100%, 0 100%);
    cursor: move;
  }
  .g6-component-toolbar {
    box-shadow: 1px 1px 5px #ccc;
    z-index: 10;
    padding: 0 5px;
    display: flex;
    flex-wrap: nowrap;
    background: #fff;
    border-right: 1px solid #ccc;
    .input-wrap {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 10;
      width: 0;
      height: 40px;
      border-radius: 4px;
      overflow: hidden;
      background-color: #fff;

      .el-input {
        padding-right: 3px;
      }

      .counter {
        font-size: 12px;
        display: flex;
        color: #999;
        font-weight: 400;
        line-height: 1.5;
        text-align: left;
        text-indent: 1em;
        .btn-control {
          /deep/ .el-button > span:hover {
            background-color: #eee;
            padding-bottom: 2px;
          }
        }
      }

      /deep/ {
        .el-input__inner {
          border-right: 0px;
          &:focus {
            border-color: transparent;
          }
        }

        .el-input__prefix {
          &:hover {
            background: #eee;
          }
        }

        .el-input-group__append {
          margin-right: 2px;
          .el-button {
            background-color: #409eff;
            color: #fff;
          }
        }
      }
    }
    .g6-component-toolbar-item {
      position: relative;
      width: 40px;
      height: 40px;
      display: flex;
      justify-content: center;
      align-items: center;
      &:hover {
        background: #eee;
        .el-button {
          color: #1890ff;
        }
      }
      .el-button {
        width: 100%;
        height: 100%;
        padding: 0;
        font-size: 20px;
        color: #666;
      }
      &::after {
        content: "";
        position: absolute;
        top: 5%;
        left: 100%;
        height: 90%;
        width: 1px;
        background-color: #ebeef5;
        z-index: 1;
      }
      &:first-child::after {
        display: none;
      }
      &:last-child::after {
        background-color: unset;
      }
    }
  }
}
</style>
